package chagine.core.util;

import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import lombok.extern.slf4j.Slf4j;
import okhttp3.*;
import okio.Buffer;
import retrofit2.Converter;
import retrofit2.Retrofit;
import retrofit2.converter.gson.GsonConverterFactory;
import retrofit2.converter.jackson.JacksonConverterFactory;

import java.nio.charset.Charset;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.TimeUnit;

@Slf4j
public class HttpGenerator {

    private static final int LIMIT_POOLS_NUM = 4;

    /**
     * 创建jackson解析的Http客户端
     */
    public static <S> S createServiceJacksonConverter(Class<S> serviceClass, String baseUrl) {
        Converter.Factory factory = JacksonConverterFactory.create();
        return createService(serviceClass, baseUrl, factory);
    }

    /**
     * 创建gson解析的Http客户端
     */
    public static <S> S createServiceGsonConverter(Class<S> serviceClass, String baseUrl) {
        Gson gson = new GsonBuilder().setLenient().create();
        Converter.Factory factory = GsonConverterFactory.create(gson);
        return createService(serviceClass, baseUrl, factory);
    }

    /**
     * 构建Okhttp请求客户端
     */
    private static <S> S createService(Class<S> serviceClass, String baseUrl, Converter.Factory factory) {
        List<Converter.Factory> converterFactoryList = new ArrayList<>();
        converterFactoryList.add(factory);
        return createService(serviceClass, baseUrl, converterFactoryList);
    }

    /**
     * 构建Okhttp请求客户端
     */
    private static <S> S createService(Class<S> serviceClass, String baseUrl, List<Converter.Factory> converterFactoryList) {
        OkHttpClient.Builder httpClientBuilder = new OkHttpClient.Builder();
        httpClientBuilder.connectTimeout(120, TimeUnit.SECONDS);
        httpClientBuilder.readTimeout(120, TimeUnit.SECONDS);
        httpClientBuilder.writeTimeout(120, TimeUnit.SECONDS);
        //自定义连接池最大空闲连接数和等待时间大小，否则默认最大5个空闲连接
        httpClientBuilder.connectionPool(new ConnectionPool(getPoolNum(Runtime.getRuntime().availableProcessors()), 5, TimeUnit.MINUTES));
        httpClientBuilder.addInterceptor(chain -> {
            long t1 = System.currentTimeMillis();
            Request original = chain.request();
            Request.Builder builder = original.newBuilder();
            String method = original.method();
            Charset charset = StandardCharsets.UTF_8;
            Request request;
            RequestBody requestBody = original.body();
            if ("get".equalsIgnoreCase(method)) {
                log.info(method + " - " + original.url());
                request = chain.request();
            } else if (requestBody == null) {
                log.info(method + " - " + original.url());
                request = chain.request();
            } else if (requestBody.contentLength() == 0) {
                log.info(method + " - " + original.url());
                request = chain.request();
            } else {
                MediaType mediaType = requestBody.contentType();
                if (mediaType != null
                        && (mediaType.toString().contains("application/json")
                        || mediaType.toString().contains("application/x-www-form-urlencoded"))) {
                    Buffer buffer = new Buffer();
                    requestBody.writeTo(buffer);
                    String content = buffer.readString(charset);
                    log.debug("request body [{}]", content);
                    log.info("\"" + method + " " + original.url() + "\" " + requestBody.contentLength() + " - " + mediaType.toString());
                    builder.method(method, requestBody);
                    request = builder.build();
                } else {
                    log.info(method + " - " + original.url());
                    request = chain.request();
                }
            }

            Headers headers = request.headers();
            log.info("http header:{}", headers.toString());

            Response response = chain.proceed(request);
            long t2 = System.currentTimeMillis();
            ResponseBody responseBody = response.peekBody(1024 * 100);
            MediaType mediaType = responseBody.contentType();
            if (mediaType != null && mediaType.toString().contains("application/json")) {
                log.info("[{}] response [{} ms]", response.request().url(), t2 - t1);
                log.debug("response body [{}]", new String(responseBody.bytes(), charset));
            }
            return response;
        });

        // 构建请求对象
        Retrofit.Builder retrofitBuilder = new Retrofit.Builder().baseUrl(baseUrl).client(httpClientBuilder.build());
        for (Converter.Factory factory : converterFactoryList) {
            retrofitBuilder.addConverterFactory(factory);
        }
        return retrofitBuilder.build().create(serviceClass);
    }

    /**
     * 计算线程数
     * 1-2 核心数：线程数 = 核心*2
     * 2-4 核心数：线程数 = 4
     * 4以上核心数：线程数 = 核心数
     *
     * @param coreNum CPU核心数
     */
    private static int getPoolNum(int coreNum) {
        int poolNum = coreNum * 2;
        if (poolNum > LIMIT_POOLS_NUM && coreNum > LIMIT_POOLS_NUM) {
            poolNum = coreNum;
        } else if (poolNum > LIMIT_POOLS_NUM) {
            poolNum = LIMIT_POOLS_NUM;
        }
        return poolNum;
    }

}
